package com.yalantis.taurus.library;

import ohos.agp.components.*;
import ohos.agp.database.DataSetSubscriber;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import static ohos.agp.utils.LayoutAlignment.TOP;

public class PullToRefreshView extends ListContainer  implements Component.LayoutRefreshedListener,
        Component.TouchEventListener{
    //下面的ItemViewType是保留值(ReservedItemViewType),如果用户的adapter与它们重复将会强制抛出异常。不过为了简化,我们检测到重复时对用户的提示是ItemViewType必须小于10000
    private static final int TYPE_REFRESH_HEADER = 10000;//设置一个很大的数字,尽可能避免和用户的adapter冲突
    private static final int TYPE_FOOTER = 10001;
    private static final int HEADER_INIT_INDEX = 10002;
    private static List<Integer> sHeaderTypes = new ArrayList<>();//每个header必须有不同的type,不然滚动的时候顺序会变化
    private static ArrayList<Component> mHeaderViews = new ArrayList<>();
    private static int height;
    private float mLastY = -1;
    private float dragRate = 3;

    private BaseItemProvider mWrapAdapter;
    private static RefreshView mRefreshHeader;
    private OnRefreshListener mLoadingListener;
    private ScrollHelper mScrollhelper;
    private DataSetSubscriber mDataObserver = new DataObserver();

    //adapter没有数据的时候显示,类似于listView的emptyView
    private Component mEmptyView;
    private static Component allComponent;
    //是否下拉刷新
    private boolean pullRefreshEnabled = true;
    private boolean refreshEnabled = false;

    public PullToRefreshView(Context context) {
        super(context);
        init(context);
    }

    public PullToRefreshView(Context context, AttrSet attrSet) {
        super(context,attrSet);
        init(context);
    }

    public PullToRefreshView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(context);
    }

    private void init(Context context){
        mHeaderViews.clear();
        mScrollhelper = new ScrollHelper();
        setTouchEventListener(this);
        setLayoutRefreshedListener(this);
        if(pullRefreshEnabled){
            mRefreshHeader = new RefreshView(context);
        }
    }

    /**
     * call it when you finish the activity,
     * when you call this,better don't call some kind of functions like
     * RefreshHeader,because the reference of mHeaderViews is NULL.
     */
    public void destroy(){
        if(mHeaderViews != null){
            mHeaderViews.clear();
            mHeaderViews = null;
        }
    }
    @SuppressWarnings("all")


    public void refresh() {
        if (pullRefreshEnabled && mLoadingListener != null) {
            mRefreshHeader.setState(RefreshView.STATE_REFRESHING);
            mLoadingListener.onRefresh();
        }
    }

    public void reset(){
        refreshComplete();
    }

    public void refreshComplete() {
        if(mRefreshHeader != null)
            mRefreshHeader.refreshComplete();
        if(mWrapAdapter != null) mWrapAdapter.notifyDataChanged();
    }

    public void setRefreshHeader(RefreshView refreshHeader) {
        mRefreshHeader = refreshHeader;
    }

    public void setPullRefreshEnabled(boolean enabled) {
        pullRefreshEnabled = enabled;
    }
    // 设置下拉时候的偏移计量因子。y = deltaY/dragRate
    // dragRate 越大，意味着，用户要下拉滑动更久来触发下拉刷新。相反越小，就越短距离
    public void setDragRate(float rate){
        if(rate <= 0.5){
            return;
        }
        dragRate = rate;
    }

    // if you can't sure that you are 100% going to
    // have no data load back from server anymore,do not use this
    @Deprecated
    public void setEmptyView(Component emptyView) {
        this.mEmptyView = emptyView;
        mDataObserver.onChanged();
    }

    @Deprecated
    public Component getEmptyView() {
        return mEmptyView;
    }

    public void setOnRefreshListener(OnRefreshListener listener) {
        mLoadingListener = listener;
    }

    public interface OnRefreshListener {

        void onRefresh();

    }

    @Override
    public void setItemProvider(BaseItemProvider itemProvider) {
        mWrapAdapter = itemProvider;
        super.setItemProvider(itemProvider);
    }

    @Override
    public BaseItemProvider getItemProvider() {
        return super.getItemProvider();
    }

    @Override
    public void onRefreshed(Component component) {
        invalidate();
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent ev) {
        int firstIndex = getItemPosByVisibleIndex(0);
        switch (ev.getAction()){
            case TouchEvent.PRIMARY_POINT_DOWN:
                mLastY = ev.getPointerScreenPosition(0).getY();
                break;
            case TouchEvent.POINT_MOVE:
                if(mRefreshHeader != null && mRefreshHeader.getState() == RefreshView.STATE_REFRESHING && mRefreshHeader.getVisibleHeight() != 0) {
                    return false;
                }
                float deltaY = ev.getPointerScreenPosition(0).getY() -mLastY;
                if( pullRefreshEnabled && firstIndex <= 1){
                    refreshEnabled = true;
                    if(mRefreshHeader == null){
                        break;
                    }
                    mRefreshHeader.onMove(deltaY / dragRate);
                    mWrapAdapter.notifyDataSetItemRangeChanged(1,2);
                    invalidate();
                    if(mRefreshHeader.getVisibleHeight() > 0 && mRefreshHeader.getState() < RefreshView.STATE_REFRESHING){
                        return true;
                    }
                } else {
                    mLastY = ev.getPointerScreenPosition(0).getY();
                }
                break;
            case TouchEvent.CANCEL:
            case TouchEvent.PRIMARY_POINT_UP:
                mLastY = -1;
                if(pullRefreshEnabled && refreshEnabled){
                    refreshEnabled = false;
                    if(mRefreshHeader != null && mRefreshHeader.releaseAction() && firstIndex == 0){
                        if(mLoadingListener != null){
                            mLoadingListener.onRefresh();
                            mWrapAdapter.notifyDataChanged();
                        }
                    } else {
                        mWrapAdapter.notifyDataChanged();
                    }
                }
                break;

        }
        return true;
    }

    public void addHeaderView(Component view) {
        if(mHeaderViews == null || sHeaderTypes == null)
            return;
        sHeaderTypes.add(HEADER_INIT_INDEX + mHeaderViews.size());
        mHeaderViews.add(view);
        if (mWrapAdapter != null) {
            mWrapAdapter.notifyDataChanged();
        }
    }

    public void removeHeaderView( Component v){
        if(mHeaderViews == null || sHeaderTypes == null || v == null)
            return;
        for (Component view : mHeaderViews) {
            if (view == v) {
                mHeaderViews.remove(view);
                break;
            }
        }
        if (mWrapAdapter != null) {
            mWrapAdapter.notifyDataChanged();
        }
    }

    public void removeAllHeaderView(){
        if(mHeaderViews == null || sHeaderTypes == null)
            return;
        mHeaderViews.clear();
        if (mWrapAdapter != null) {
            mWrapAdapter.notifyDataChanged();
        }
    }

    /** =======================================================  ItemProvider start======================================================= */
    public static abstract class RecyclerAdapter<T> extends BaseItemProvider {
        private Context context;
        private List<T> data;
        private int mLayoutId;
        private int numColumns = 1;
        private OnItemClickListener onItemClickListener;

        public RecyclerAdapter(Context context, List<T> data, int mLayoutId) {
            this.context = context;
            this.data = data;
            this.mLayoutId = mLayoutId;
        }

        public List<T> getData() {
            return data;
        }

        @Override
        public int getCount() {
            int adjLen = 1+mHeaderViews.size();
            return (data.size() % numColumns == 0 ? data.size() / numColumns : data.size() / numColumns + 1)+adjLen;
        }

        @Override
        public T getItem(int position) {
            return data.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public int getItemComponentType(int position) {
            if(position == getCount() -1){
                return TYPE_FOOTER;
            }
            return super.getItemComponentType(position);
        }

        @Override
        public Component getComponent(int position, Component convertComponent, ComponentContainer parent) {
            if(position == 0 ){
                return mRefreshHeader;
            } else if(position > 0 && position < mHeaderViews.size() + 1){
                Component component = mHeaderViews.get(position-1);
                return component;
            }

            ViewHolder viewHolder;
            convertComponent = new DirectionalLayout(context);
            ((DirectionalLayout) convertComponent).setOrientation(Component.HORIZONTAL);
            ComponentContainer.LayoutConfig layoutConfig = convertComponent.getLayoutConfig();
            layoutConfig.width = DependentLayout.LayoutConfig.MATCH_PARENT;
            convertComponent.setLayoutConfig(layoutConfig);

            for (int i = 0; i < numColumns; i++) {
                if ((position -1 ) * numColumns + i < data.size()) {
                    DirectionalLayout dlItemParent = new DirectionalLayout(context);
                    dlItemParent.setLayoutConfig(new DirectionalLayout.LayoutConfig(0, DirectionalLayout.LayoutConfig.MATCH_CONTENT, TOP, 1));
                    height = dlItemParent.getHeight();
                    Component childConvertComponent = LayoutScatter.getInstance(context).parse(mLayoutId, null, false);
                    int finalI = i;
                    childConvertComponent.setClickedListener(new Component.ClickedListener() {
                        @Override
                        public void onClick(Component component) {
                            onItemClickListener.onItemClick(component, (position -1) * numColumns + finalI);
                        }
                    });
                    dlItemParent.addComponent(childConvertComponent);
                    ((ComponentContainer) convertComponent).addComponent(dlItemParent);
                    viewHolder = new ViewHolder(childConvertComponent);
                    bind(viewHolder, getItem((position -1) * numColumns + i), (position -1) * numColumns + i);
                }
                else {
                    Component childConvertComponent = new Component(context);
                    DirectionalLayout.LayoutConfig layoutConfig1 = new DirectionalLayout.LayoutConfig(0, height);
                    layoutConfig1.weight = 1;
                    childConvertComponent.setLayoutConfig(layoutConfig1);
                    ((ComponentContainer) convertComponent).addComponent(childConvertComponent);
                }
            }
            return convertComponent;
        }

        public void setNumColumns(int numColumns) {
            this.numColumns = numColumns;
        }

        public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
            this.onItemClickListener = onItemClickListener;
        }

        public abstract void bind(ViewHolder holder, T s, int position);

        public  class ViewHolder  {
            HashMap<Integer, Component> mViews = new HashMap<>();
            public Component itemView;
            public ViewHolder(Component component) {
                this.itemView = component;
            }

            public <E extends Component> E getView(int viewId) {
                E view = (E) mViews.get(viewId);
                if (view == null) {
                    view = (E) itemView.findComponentById(viewId);
                    mViews.put(viewId, view);
                }
                return view;
            }
        }

        public  abstract static class OnItemClickListener {
            public abstract void onItemClick(Component component, int position);
        }

    }

    /** =======================================================  ItemProvider end======================================================= */
    private class DataObserver extends DataSetSubscriber{
        @Override
        public void onChanged() {
            if(mWrapAdapter != null){
                mWrapAdapter.notifyDataChanged();
            }
        }

        @Override
        public void onItemChanged(int position) {
            mWrapAdapter.notifyDataSetItemChanged(position);
        }

        @Override
        public void onItemInserted(int position) {
            mWrapAdapter.notifyDataSetItemInserted(position);
        }

        @Override
        public void onItemRemoved(int position) {
            mWrapAdapter.notifyDataSetItemRemoved(position);
        }

        @Override
        public void onItemRangeChanged(int startPos, int countItems) {
            mWrapAdapter.notifyDataSetItemRangeChanged(startPos,countItems);
        }

        @Override
        public void onItemRangeInserted(int startPos, int countItems) {
            mWrapAdapter.notifyDataSetItemRangeInserted(startPos,countItems);
        }

        @Override
        public void onItemRangeRemoved(int startPos, int countItems) {
            mWrapAdapter.notifyDataSetItemRangeRemoved(startPos,countItems);
        }
    }
}
